﻿using CKS.Core.Framework;
using CKS.PLC.Core;
using CKS.PLC.Core.Event;
using CKS.PLC.Core.Siemens;
using Google.Protobuf.WellKnownTypes;
using Infrastructure;
using IP2Region.Ex.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using S7.Net;
using S7.Net.Types;
using SqlSugar;
using SqlSugar.Extensions;
using SqlSugar.IOC;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using System.Security.Cryptography;
using System.Security.Policy;
using System.Text;
using System.Threading.Tasks;
using ZR.Model.Models.ProductionControl;


namespace CKS.PLC.Core.Siemens
{
    /// <summary>
    /// plc客户端
    /// 主要功能，读取订阅点信息，调用处理类进行订阅点的处理
    /// </summary>
    public class S7NetClient : SiemensClientBase<S7.Net.Plc>
    {
        private bool FirstConnect = false;

        public S7NetClient(PlcConfig config, IServiceProvider serviceProvider) : base(config, serviceProvider)
        {

        }

        public override Plc GetSdkInstance()
        {
            PlcSession = PlcInfo.CpuType switch
            {
                "1500" => new Plc(CpuType.S71500, PlcInfo.PlcAddress, 0, 1),
                _ => new Plc(CpuType.S71200, PlcInfo.PlcAddress, 0, 1)
            };
            PlcSession.ReadTimeout = 2000;
            PlcSession.WriteTimeout = 2000;
            return PlcSession;
        }

        public override async Task ConnectPlc(CancellationToken cancellation)
        {
            try
            {
                if (!PlcSession.IsConnected)
                {
                    await PlcSession.OpenAsync(cancellation);
                }
                if (IsConnected == false && PlcSession.IsConnected)
                {
                    EventBus.AddLogInfoToQueues($"{PlcInfo.WorkstationCode}:{PlcSession.IP} 连接成功");
                }
                IsConnected = PlcSession.IsConnected;
                
            }
            catch (Exception ex)
            {
                if (IsConnected)
                {
                    EventBus.AddLogInfoToQueues($"{PlcInfo.WorkstationCode}:{PlcSession.IP} 连接已断开");
                }
                else if(!FirstConnect)
                {
                    EventBus.AddLogInfoToQueues($"{PlcInfo.WorkstationCode}:{PlcSession.IP} 连接失败");
                }
                IsConnected = false;
                if (!PlcInfo.PlcAddress.Equals(PlcSession.IP))
                {
                    EventBus.AddLogInfoToQueues($"{PlcInfo.WorkstationCode} PLC/IP变化");
                    UpdatePlcSession();
                }
            }
            //首次连接状态
            if (!FirstConnect)
                FirstConnect = true;
        }

        public override void WriteInt16(int db, int startByteAdr, Int16 value)
        {
            PlcSession.Write(DataType.DataBlock, db, startByteAdr, value); 
        }

        public override short ReadInt16(int db, int startByteAdr)
        {
            return (short)PlcSession.Read(DataType.DataBlock, db, startByteAdr, VarType.Int, 1);
        }

        public override async Task<byte[]> ReadBytesAsync(int db, int startByteAdr, int count)
        {
           return await PlcSession.ReadBytesAsync(DataType.DataBlock, db, startByteAdr, count);
        }

        /// <summary>
        /// 从缓存读取数据
        /// </summary>
        public override object GetValue(CksPlcDataPosition s7Point)
        {
           var cache = GetCacheValues(s7Point.BlockDb);
            if (cache != null) {
                var p = cache.Find(a => a.Position.Id == s7Point.Id);
                if (p != null)
                {
                    return p.DecodeObject.IsCharArray ? p.DecodeObject.CharString : p.DecodeObject.Value;
                }
            }
            return null;
        }

        public override void SetValue(CksPlcDataPosition one, string Value)
        {
            try
            {
                string pt = one.ParseType;
                if (pt.Contains("["))
                {
                    pt = pt.Substring(0, pt.IndexOf("["));
                }

                //char数组特殊处理
                if (SieminDecoder.IsArrayType(one.ParseType, out int len, out string tt)) {
                    if (tt == "char") {
                        //说明是char数组
                        byte[] result = new byte[len];
                        byte[] buffer = Encoding.ASCII.GetBytes(Value);
                        // 只复制前len个字节
                        Array.Copy(buffer, result, Math.Min(buffer.Length, len));
                        PlcSession.WriteBytes(DataType.DataBlock, one.BlockDb,
                           (int)one.BytePosition, result);
                        return;
                    }
                }


                switch (pt)
                {
                    case "int":
                    case "int16":  
                        PlcSession.Write(one.Address, Convert.ToInt16(Value));
                        break;
                    case "word": // ushort
                        PlcSession.Write(one.Address, Convert.ToUInt16(Value));
                        break;
                    case "char":
                        if (Value.Length != 1)
                            throw new Exception("写入字符长度非法");
                        PlcSession.Write(one.Address, Encoding.ASCII.GetBytes(Value)[0]);
                        break;
                    case "string":
                        WriteString(one.BlockDb, (int)one.BytePosition, Value, one.ByteLength);
                        break;
                    case "float":
                    case "real":
                        PlcSession.Write(one.Address, Convert.ToSingle(Value));
                        break;
                    case "lreal":
                        PlcSession.Write(one.Address, Convert.ToDouble(Value));
                        break;
                    case "byte":
                        PlcSession.Write(one.Address, Convert.ToByte(Value));
                        break;
                    case "bool":
                        PlcSession.Write(one.Address, Value.ToBool());
                        break;
                    case "dtl":
                        PlcSession.WriteBytes(DataType.DataBlock, one.BlockDb,
                            (int)one.BytePosition,
                            DateTimeLong.ToByteArray(Convert.ToDateTime(Value)));
                        break;
                    case "dt":
                        PlcSession.WriteBytes(DataType.DataBlock, one.BlockDb,
                            (int)one.BytePosition,
                            S7.Net.Types.DateTime.ToByteArray(Convert.ToDateTime(Value)));
                        break;
                    default:
                        throw new Exception($"未实现，{pt}，该数据类型的写入方法！");
                }
            }
            catch (Exception ex)
            {
                throw new Exception("写入异常:" + ex.Message);
            }
        }

        private void UpdatePlcSession()
        {
            PlcSession = PlcInfo.CpuType switch
            {
                "1500" => new Plc(CpuType.S71500, PlcInfo.PlcAddress, 0, 1),
                _ => new Plc(CpuType.S71200, PlcInfo.PlcAddress, 0, 1)
            };
            PlcSession.ReadTimeout = 2000;
            PlcSession.WriteTimeout = 2000;
        }

        private void WriteString(int db, int start, string value, int lenth)
        {
            // 编码为字节数组
            byte[] stringBytes = Encoding.ASCII.GetBytes(value);
            // 构建带控制信息的字节数组
            byte[] dataString = new byte[lenth]; // 加2是为了存储控制信息
                                                 // 添加控制信息
            dataString[0] = (byte)(lenth - 2); //第一个字节是字符串的字节总数
            dataString[1] = (byte)(stringBytes.Length); // 第二个字节表示实际使用字节数量
                                                        // 复制字符串数据到字节数组
            Array.Copy(stringBytes, 0, dataString, 2, stringBytes.Length);
            // 将字节数组写入PLC
            PlcSession.WriteBytes(S7.Net.DataType.DataBlock, db, start, dataString);
        }
    }
}
